package island

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
)

type dbItem struct {
	container   *base
	StoragePath string `json:"storagePath"`
}

type islandDB struct {
	sync.Map
}

var db = new(islandDB)

func initDB() {
	db.init()
}

func (db *islandDB) init() {
	logger.Debug("load island database from disk")
	data, err := ioutil.ReadFile(filepath.Join(containerRootDir, "containers.json"))
	if err != nil {
		if os.IsExist(err) {
			logger.WithError(err).Error("failed to load island database")
		} else {
			logger.WithError(err).Warning("island database doesn't exist")
		}
	} else {
		tempDB := make(map[string]*dbItem)
		counts := 0
		if err = json.Unmarshal(data, &tempDB); err != nil {
			logger.WithError(err).Error("failed to parse container database")
		}
		for _, item := range tempDB {
			path := item.StoragePath
			container := recoverContainerFromFile(path)

			if container != nil {
				db.add(container, path)
				counts += 1
			}

		}
		logger.Printf("found %d containers", counts)
	}
	// 更新数据库
	db.save(false, "")
}

func (db *islandDB) add(container *base, path string) {
	db.Store(container.ID, &dbItem{
		container:   container,
		StoragePath: path,
	})
}

func (db *islandDB) save(saveItem bool, id string) {
	logger.Debug("saving container database")
	data, _ := json.Marshal(db.convertToMap())
	err := ioutil.WriteFile(filepath.Join(containerRootDir, "containers.json"), data, 0600)
	if err != nil {
		logger.WithError(err).Error("cannot save container database file to disk")
	}

	if saveItem {
		db.saveItem(id)
	}
}

func (db *islandDB) getItem(id string) (*dbItem, bool) {
	if value, ok := db.Load(id); ok {
		item := value.(*dbItem)
		return item, true
	}
	return nil, false
}

func (db *islandDB) convertToMap() map[string]*dbItem {
	m := make(map[string]*dbItem)
	db.Range(func(k, v interface{}) bool {
		id, item := k.(string), v.(*dbItem)
		m[id] = item
		return true
	})
	return m
}

func (db *islandDB) saveItem(id string) {
	logger.Debug("saving a container")
	if item, ok := db.getItem(id); ok {
		data, _ := json.Marshal(item.container)
		err := ioutil.WriteFile(item.StoragePath, data, 0600)
		if err != nil {
			logger.WithError(err).Error("cannot save container database file to disk")
		}
	}

}

func (db *islandDB) delete(id string) {
	db.Delete(id)
}
